﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Security.Cryptography;
using System.Xml.Linq;
using System.IO;
using System.Security.Cryptography.Xml;

namespace XUtility
{
    public abstract class SecurityClass
    {
        #region 通用方法

        /// <summary>
        /// 常用加密方法
        /// </summary>
        /// <param name="EncType">加密算法</param>
        /// <param name="str">待加密字符串</param>
        /// <returns></returns>
        private static String BaseEncrypt(HashAlgorithm EncType, String str)
        {
            if (!str.HasValue())
            {
                return "";
            }
            var m_data = Encoding.UTF8.GetBytes(str);
            var by = EncType.ComputeHash(m_data);

            return BitConverter.ToString(by).Replace("-", "").ToLower();
        }


        #endregion

        #region MD5加密
        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="strIN">待加密字符串</param>
        /// <returns></returns> 
        public static String MD5Encrypt(String strIN)
        {
            var encType = new MD5CryptoServiceProvider();
            return BaseEncrypt(encType, strIN);
        }
        #endregion

        #region AES加密
        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="plainText">待加密字段</param>
        /// <param name="key">密钥</param>
        /// <param name="iv">偏移量</param>
        /// <returns></returns>
        public static String AESEncryptToString(String plainText, String key, String iv)
        {
            byte[] re = AESEncryptToBytes(plainText, key, iv);

            return Encoding.Default.GetString(re);
        }
        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="plainText">待加密字段</param>
        /// <param name="key">密钥</param>
        /// <param name="iv">偏移量</param>
        /// <returns></returns>
        public static String AESEncryptToString(String plainText, byte[] key, byte[] iv)
        {
            byte[] re = AESEncryptToBytes(plainText, key, iv);

            return Encoding.Default.GetString(re);
        }
        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="plainText">待加密字段</param>
        /// <param name="key">密钥</param>
        /// <param name="iv">偏移量</param>
        /// <returns></returns>
        public static byte[] AESEncryptToBytes(String plainText, String key, String iv)
        {
            byte[] b_key = Encoding.Default.GetBytes(key);
            byte[] b_iv = Encoding.Default.GetBytes(iv);
            return AESEncryptToBytes(plainText, b_key, b_iv);
        }
        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="plainText">待加密字段</param>
        /// <param name="key">密钥</param>
        /// <param name="iv">偏移量</param>
        /// <returns></returns>
        public static byte[] AESEncryptToBytes(string plainText, byte[] key, byte[] iv)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("key");
            if (iv == null || iv.Length <= 0)
                throw new ArgumentNullException("iv");
            byte[] encrypted;
            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key;
                aesAlg.IV = iv;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }


            // Return the encrypted bytes from the memory stream.
            return encrypted;

        }

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="cipherText">已加密内容</param>
        /// <param name="key">密钥</param>
        /// <param name="iv">偏移量</param>
        /// <returns></returns>
        public static String AESDecrypt(String cipherText, String key, String iv)
        {
            byte[] b = Encoding.Default.GetBytes(cipherText);
            byte[] b_key = Encoding.Default.GetBytes(key);
            byte[] b_iv = Encoding.Default.GetBytes(iv);
            return AESDecryptStringFromBytes(b, b_key, b_iv);
        }
        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="cipherText">已加密内容</param>
        /// <param name="key">密钥</param>
        /// <param name="iv">偏移量</param>
        /// <returns></returns>
        public static String AESDecrypt(String cipherText, byte[] key, byte[] iv)
        {
            byte[] b = Encoding.Default.GetBytes(cipherText);
            return AESDecryptStringFromBytes(b, key, iv);
        }
        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="cipherText">已加密内容</param>
        /// <param name="key">密钥</param>
        /// <param name="iv">偏移量</param>
        /// <returns></returns>
        public static String AESDecrypt(byte[] cipherText, byte[] key, byte[] iv)
        {
            return AESDecryptStringFromBytes(cipherText, key, iv);
        }
        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="cipherText">已加密内容</param>
        /// <param name="key">密钥</param>
        /// <param name="iv">偏移量</param>
        /// <returns></returns>
        public static string AESDecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("key");
            if (iv == null || iv.Length <= 0)
                throw new ArgumentNullException("iv");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key;
                aesAlg.IV = iv;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }

            }

            return plaintext;

        }

        #endregion

        #region SHA1加密
        /// <summary>
        /// SHA1加密
        /// </summary>
        /// <param name="strIN">待加密字符串</param>
        /// <returns></returns>
        public static String SHA1Encrypt(String strIN)
        {
            var encType = new SHA1CryptoServiceProvider();
            return BaseEncrypt(encType, strIN);
        }
        #endregion

        #region SHA256加密
        /// <summary>
        /// SHA256加密
        /// </summary>
        /// <param name="strIN">待加密字符串</param>
        /// <returns></returns>
        public static String SHA256Encrypt(String strIN)
        {
            var encType = new SHA256Managed();
            return BaseEncrypt(encType, strIN);

        }
        #endregion

        #region SHA512加密
        /// <summary>
        /// SHA512加密
        /// </summary>
        /// <param name="strIN">待加密字符串</param>
        /// <returns></returns>
        public static String SHA512Encrypt(String strIN)
        {
            var encType = new SHA512Managed();
            return BaseEncrypt(encType, strIN);
        }
        #endregion

        #region RSA加密(非对称)
        // 加密 钥匙
        private const String _rsaEncryptKey = @"<RSAKeyValue><Modulus>5m9m14XH3oqLJ8bNGw9e4rGpXpcktv9MSkHSVFVMjHbfv+SJ5v0ubqQxa5YjLN4vc49z7SVju8s0X4gZ6AzZTn06jzWOgyPRV54Q4I0DCYadWW4Ze3e+BOtwgVU1Og3qHKn8vygoj40J6U85Z/PTJu3hN1m75Zr195ju7g9v4Hk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
        // 解密 私钥
        private const String _rsaDecryptKey = @"<RSAKeyValue><Modulus>5m9m14XH3oqLJ8bNGw9e4rGpXpcktv9MSkHSVFVMjHbfv+SJ5v0ubqQxa5YjLN4vc49z7SVju8s0X4gZ6AzZTn06jzWOgyPRV54Q4I0DCYadWW4Ze3e+BOtwgVU1Og3qHKn8vygoj40J6U85Z/PTJu3hN1m75Zr195ju7g9v4Hk=</Modulus><Exponent>AQAB</Exponent><P>/hf2dnK7rNfl3lbqghWcpFdu778hUpIEBixCDL5WiBtpkZdpSw90aERmHJYaW2RGvGRi6zSftLh00KHsPcNUMw==</P><Q>6Cn/jOLrPapDTEp1Fkq+uz++1Do0eeX7HYqi9rY29CqShzCeI7LEYOoSwYuAJ3xA/DuCdQENPSoJ9KFbO4Wsow==</Q><DP>ga1rHIJro8e/yhxjrKYo/nqc5ICQGhrpMNlPkD9n3CjZVPOISkWF7FzUHEzDANeJfkZhcZa21z24aG3rKo5Qnw==</DP><DQ>MNGsCB8rYlMsRZ2ek2pyQwO7h/sZT8y5ilO9wu08Dwnot/7UMiOEQfDWstY3w5XQQHnvC9WFyCfP4h4QBissyw==</DQ><InverseQ>EG02S7SADhH1EVT9DD0Z62Y0uY7gIYvxX/uq+IzKSCwB8M2G7Qv9xgZQaQlLpCaeKbux3Y59hHM+KpamGL19Kg==</InverseQ><D>vmaYHEbPAgOJvaEXQl+t8DQKFT1fudEysTy31LTyXjGu6XiltXXHUuZaa2IPyHgBz0Nd7znwsW/S44iql0Fen1kzKioEL3svANui63O3o5xdDeExVM6zOf1wUUh/oldovPweChyoAdMtUzgvCbJk1sYDJf++Nr0FeNW1RB1XG30=</D></RSAKeyValue>";

        #region ============================  加密  ============================
        /// <summary>
        /// RSA加密 (非对称)
        /// <para>待加密的字节数不能超过密钥的长度值除以 8 再减去 11（即：RSACryptoServiceProvider.KeySize / 8 - 11）， </para>
        /// <para>而加密后得到密文的字节数，正好是密钥的长度值除以 8（即：RSACryptoServiceProvider.KeySize / 8）。</para>
        /// <para>就是字节长度不能超过117</para>
        /// </summary>
        /// <param name="originalValue">待加密字符串</param>
        /// <param name="key">公钥</param> 
        /// <returns></returns>
        public static String RSAEncrypt(String originalValue, String key = SecurityClass._rsaEncryptKey)
        {
            String st = String.Empty;
            if (originalValue.HasValue())
            {
                RSACryptoServiceProvider crypt = new RSACryptoServiceProvider();
                UTF8Encoding enc = new UTF8Encoding();
                Byte[] bytes = enc.GetBytes(originalValue);
                crypt.FromXmlString(key);
                bytes = crypt.Encrypt(bytes, false);
                st = Convert.ToBase64String(bytes);
            }
            return st;
        }
        /// <summary>
        /// RSA分段加密 (非对称)
        /// </summary>
        /// <param name="plaintext">待加密字符串</param>
        /// <param name="key">公钥</param> 
        /// <returns></returns>
        public static String RSASectionEncrypt(String plaintext, String key = SecurityClass._rsaEncryptKey)
        {
            String s = String.Empty;
            if (plaintext.HasValue())
            {
                using (RSACryptoServiceProvider RSACryptography = new RSACryptoServiceProvider())
                {
                    RSACryptography.FromXmlString(key);
                    Byte[] PlaintextData = Encoding.UTF8.GetBytes(plaintext);
                    int MaxBlockSize = RSACryptography.KeySize / 8 - 11;    //加密块最大长度限制

                    if (PlaintextData.Length <= MaxBlockSize)
                        s = Convert.ToBase64String(RSACryptography.Encrypt(PlaintextData, false));
                    else
                    {
                        using (MemoryStream PlaiStream = new MemoryStream(PlaintextData))
                        {
                            using (MemoryStream CrypStream = new MemoryStream())
                            {
                                Byte[] Buffer = new Byte[MaxBlockSize];
                                int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);

                                while (BlockSize > 0)
                                {
                                    Byte[] ToEncrypt = new Byte[BlockSize];
                                    Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);

                                    Byte[] Cryptograph = RSACryptography.Encrypt(ToEncrypt, false);
                                    CrypStream.Write(Cryptograph, 0, Cryptograph.Length);

                                    BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                                }
                                s = Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
                            }
                        }
                    }
                }
            }
            return s;
        }
        #endregion

        #region ============================  解密  ============================
        /// <summary>
        /// RSA解密 (非对称)
        /// <para>待解密的字节数不能超过密钥的长度值除以 8 再减去 11（即：RSACryptoServiceProvider.KeySize / 8 - 11）， </para>
        /// <para>而加密后得到密文的字节数，正好是密钥的长度值除以 8（即：RSACryptoServiceProvider.KeySize / 8）。</para>
        /// <para>就是字节长度不能超过117</para>
        /// </summary>
        /// <param name="encryptedValue">待解密字符串</param>
        /// <param name="key">私钥</param>
        /// <returns></returns>
        public static String RSADecrypt(String encryptedValue, String key = SecurityClass._rsaDecryptKey)
        {
            try
            {
                String s = String.Empty;
                if (encryptedValue.HasValue())
                {
                    RSACryptoServiceProvider crypt = new RSACryptoServiceProvider();
                    crypt.FromXmlString(key);
                    UTF8Encoding enc = new UTF8Encoding();
                    Byte[] bytes = Convert.FromBase64String(encryptedValue);
                    bytes = crypt.Decrypt(bytes, false);
                    s = enc.GetString(bytes);
                }
                return s;
            }
            catch
            {
                return String.Empty;
            }
        }
        /// <summary>
        /// RSA分段解密 (非对称)
        /// </summary>
        /// <param name="ciphertext">待解密字符串</param>
        /// <returns></returns>
        public static String RSASectionDecrypt(String ciphertext, String key = SecurityClass._rsaDecryptKey)
        {
            String s = String.Empty;
            if (ciphertext.HasValue())
            {
                using (RSACryptoServiceProvider RSACryptography = new RSACryptoServiceProvider())
                {
                    RSACryptography.FromXmlString(key);
                    Byte[] CiphertextData = Convert.FromBase64String(ciphertext);
                    int MaxBlockSize = RSACryptography.KeySize / 8;    //解密块最大长度限制

                    if (CiphertextData.Length <= MaxBlockSize)
                        s = Encoding.UTF8.GetString(RSACryptography.Decrypt(CiphertextData, false));
                    else
                    {
                        using (MemoryStream CrypStream = new MemoryStream(CiphertextData))
                        {
                            using (MemoryStream PlaiStream = new MemoryStream())
                            {
                                Byte[] Buffer = new Byte[MaxBlockSize];
                                int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);

                                while (BlockSize > 0)
                                {
                                    Byte[] ToDecrypt = new Byte[BlockSize];
                                    Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize);

                                    Byte[] Plaintext = RSACryptography.Decrypt(ToDecrypt, false);
                                    PlaiStream.Write(Plaintext, 0, Plaintext.Length);

                                    BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
                                }
                                s = Encoding.UTF8.GetString(PlaiStream.ToArray());
                            }
                        }
                    }
                }
            }
            return s;
        }
        #endregion

        #endregion

        #region DES加密（对称）

        #region ============================  加密  ============================
        /// <summary>
        /// 使用DES加密（对称）
        /// </summary>
        /// <param name="str">待加密的字符串</param>
        /// <param name="Key">密钥(最大长度8)</param>
        /// <returns>加密后的字符串</returns>
        public static String DESEncrypt(String str, String Key)
        {
            var IV = new byte[] { 0x14, 0xFC, 0x29, 0xC8, 0x9A, 0xCB, 0xCA, 0xE7 };
            var bKey = Encoding.UTF8.GetBytes(Key.PadRight(8, '0').Substring(0, 8));
            var bIV = IV;
            var bStr = Encoding.UTF8.GetBytes(str);
            try
            {
                DESCryptoServiceProvider desc = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, desc.CreateEncryptor(bKey, bIV), CryptoStreamMode.Write);
                cStream.Write(bStr, 0, bStr.Length);
                cStream.FlushFinalBlock();
                return Convert.ToBase64String(mStream.ToArray());
            }
            catch
            {
                return string.Empty;
            }
        }
        #endregion

        #region ============================  解密  ============================
        /// <summary>
        /// 使用DES解密（对称）
        /// </summary>
        /// <param name="encryptedValue">待解密的字符串</param>
        /// <param name="key">密钥(最大长度8)</param>
        /// <param name="iv">m初始化向量(最大长度8)</param>
        /// <returns>解密后的字符串</returns>
        public static String DESDecrypt(String str, String Key)
        {
            try
            {
                var IV = new byte[] { 0x14, 0xFC, 0x29, 0xC8, 0x9A, 0xCB, 0xCA, 0xE7 };
                var bKey = Encoding.UTF8.GetBytes(Key.PadRight(8, '0').Substring(0, 8));
                var bIV = IV;
                var bStr = Convert.FromBase64String(str);
                DESCryptoServiceProvider desc = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, desc.CreateDecryptor(bKey, bIV), CryptoStreamMode.Write);
                cStream.Write(bStr, 0, bStr.Length);
                cStream.FlushFinalBlock();
                return Encoding.UTF8.GetString(mStream.ToArray());
            }
            catch
            {
                return string.Empty;
            }

        }
        #endregion

        #endregion

        #region 对称加密 XML 文件

        #region ----------------------加密------------------------
        /// <summary>
        /// 使用对称加密 XML 文件,
        /// <para>需要返回值，因为实参已被改变</para>
        /// </summary>
        /// <param name="xDoc">待加密的文件</param>
        /// <param name="ElementToEncrypt">文件中待加密行的关键字</param>
        /// <param name="Alg">TripleDES key</param>
        /// <param name="KeyName">密匙名</param>
        public static XDocument EncryptXml(XDocument xDoc, String ElementToEncrypt, SymmetricAlgorithm Alg, String KeyName = "OSXml")
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xDoc.Document.ToString());
            EncryptXml(doc, ElementToEncrypt, Alg, KeyName);
            using (XmlNodeReader reader = new XmlNodeReader(doc))
                return xDoc = XDocument.Load(reader, LoadOptions.None);
        }
        /// <summary>
        /// 使用对称加密 XML 文件
        /// </summary>
        /// <param name="Doc">待加密的文件</param>
        /// <param name="ElementToEncrypt">文件中待加密行的关键字</param>
        /// <param name="Alg">TripleDES key</param>
        /// <param name="KeyName">密匙名</param>
        public static void EncryptXml(XmlDocument Doc, String ElementToEncrypt, SymmetricAlgorithm Alg, String KeyName = "OSXml")
        {
            // Check the arguments.  
            if (Doc == null)
                throw new ArgumentNullException("Doc");
            if (ElementToEncrypt == null)
                throw new ArgumentNullException("ElementToEncrypt");
            if (Alg == null)
                throw new ArgumentNullException("TripleDESCryptoServiceProvider");

            ////////////////////////////////////////////////
            // Find the specified element in the XmlDocument
            // object and create a new XmlElemnt object.
            ////////////////////////////////////////////////

            XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;

            // Throw an XmlException if the element was not found.
            if (elementToEncrypt == null)
                throw new XmlException("The specified element was not found");

            //////////////////////////////////////////////////
            // Create a new instance of the EncryptedXml class 
            // and use it to encrypt the XmlElement with the 
            // symmetric key.
            //////////////////////////////////////////////////

            EncryptedXml eXml = new EncryptedXml();

            // Add the key mapping.
            eXml.AddKeyNameMapping(KeyName, Alg);

            // Encrypt the element.
            EncryptedData edElement = eXml.Encrypt(elementToEncrypt, KeyName);

            ////////////////////////////////////////////////////
            // Replace the element from the original XmlDocument
            // object with the EncryptedData element.
            ////////////////////////////////////////////////////

            EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);

        }
        #endregion

        #region ----------------------解密------------------------
        /// <summary>
        /// 使用对称解密 XML 文件
        /// <para>需要返回值，因为实参已被改变</para>
        /// </summary>
        /// <param name="xDoc">待解密文件</param>
        /// <param name="Alg">TripleDES key</param>
        /// <param name="KeyName">密匙名</param>
        public static XDocument DecryptXml(XDocument xDoc, SymmetricAlgorithm Alg, String KeyName = "OSXml")
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xDoc.Document.ToString());
            DecryptXml(doc, Alg, KeyName);
            using (XmlNodeReader reader = new XmlNodeReader(doc))
                return xDoc = XDocument.Load(reader, LoadOptions.None);
        }
        /// <summary>
        /// 使用对称解密 XML 文件
        /// </summary>
        /// <param name="Doc">待解密文件</param>
        /// <param name="Alg">TripleDES key</param>
        /// <param name="KeyName">密匙名</param>
        public static void DecryptXml(XmlDocument Doc, SymmetricAlgorithm Alg, String KeyName = "OSXml")
        {
            // Check the arguments.  
            if (Doc == null)
                throw new ArgumentNullException("Doc");
            if (Alg == null)
                throw new ArgumentNullException("TripleDESCryptoServiceProvider");

            // Create a new EncryptedXml object.
            EncryptedXml exml = new EncryptedXml(Doc);

            // Add the key name mapping.
            exml.AddKeyNameMapping(KeyName, Alg);

            // Decrypt the XML document.
            exml.DecryptDocument();
        }
        #endregion

        #endregion
    }
}
